package com.jt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.concurrent.Executor;

@Component  //将给类交给Spring容器管理
@Aspect     //表示该类是一个切面
@Order(2)   //排序执行，按照顺序执行
public class SpringAOP {

    /**
     * 知识回顾：AOP利用动态代理  扩展目标方法
     * 公式：  切面 = 切入点表达式 + 通知方法
     * 切入点表达式：如果目标对象满足切入点表达式得判断（if）
     *            则Spring自动为其创建代理对象
     * 通知方法：对目标方法进行扩展的封装方法
     * 目标对象的bean的ID：userServiceImpl
     * 切入点表达式：
     *      1.bean("bean的id")
     * AOP规则：如果目标对象满足切入点表达式，则执行通知方法
     */

//    @Pointcut("bean(userServiceImpl)")
    //按照类型匹配，可以使用通配符*:只拦截UserServiceImp的类
//    @Pointcut("within(com.jt.service.UserServiceImpl)")
    //按照类型匹配，可以使用通配符*:只拦截com.jt.service下的类
//    @Pointcut("within(com.jt.service.*)")
    //按照类型匹配，可以使用通配符*:只拦截com.jt.service下所有包下的所有类
//    @Pointcut("within(com.jt.service..*)")
    //拦截com.任意包.service下所有包下的所有类
//    @Pointcut("within(com.*.service..*)")
//    @Pointcut("execution(返回值类型 包名.类名.方法名(参数列表))")
    //匹配指定类下的方法
//    @Pointcut("execution(* com.jt.service.UserServiceImpl.addUser())")
//    @Pointcut("execution(* com.jt.service..*.*(..))")
    //可以根据用户的自定义注解进行拦截
    @Pointcut("@annotation(com.jt.anno.Wbh)")
    public void pointcut(){

    }

    //1.前置通知：在目标方法执行之前执行
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){  //连接点：获取方法中的数据
        //获取目标对象的类型
        Class<?> targetClass = joinPoint.getTarget().getClass();
        //获取方法名称
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringTypeName();
        Object[] args = joinPoint.getArgs();
        System.out.println("我是前置通知!!!");
        System.out.println("类型："+targetClass);
        System.out.println("方法名称："+methodName);
        System.out.println("类的名称："+className);
        System.out.println("方法中携带的参数"+ Arrays.toString(args));
    }

    //2.后置通知：在目标方法执行之后执行
    //通过returning=“result”属性，获取目标方法的返回值，当作参数传递给result
    @AfterReturning(value = "pointcut()",returning = "result")
    public void afterReturn(Object result){
        System.out.println("我是后置通知!!!");
        System.out.println("用户的返回值："+result);
    }

    //3.异常通知：目标方法执行报错，执行改通知
    @AfterThrowing(value = "pointcut()",throwing = "exception")
    public void afterThrowing(Exception exception){
        System.out.println("添加数据输入错误");
        System.out.println(" 获取异常信息"+exception.getMessage());
        exception.printStackTrace();
    }

    //4.最终通知：目标方法之后都要执行的通知
    @After("pointcut()")
    public void after(){
        System.out.println("我是最终通知");
    }

    //5.重点掌握，环绕通知：在目标方法执行前后都要执行。控制目标方法
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知A方法执行前");
        //底层调用动态代理的invoke方法，执行目标方法
        Object result = joinPoint.proceed();
        System.out.println("环绕通知A方法执行后");
        return result;
    }

}
